//
// Created by root on 19-7-30.
//

#include "hook_sys_properties.h"
#include "log_utils.h"
#include "process_utils.h"
#include "inject_compacter.h"
#include "jni_utils.h"
#include "string_utils.h"
#include "AndHook.h"
#include <sys/system_properties.h>
#include <cstring>
#include <unistd.h>
#include <dlfcn.h>
#include <cstdlib>
#include <random>

#define TAG "SystemProperties"

char* generate_random_prop_value(int length);

#define OPERATION_NAME "__system_properties_get"

inline bool need_auth(JNIEnv *env, const char* prop_name, const char* target,const char* exclude_process) {
    if(is_exclude_process(exclude_process))
        return true;
    if (strstr(prop_name, target) != nullptr) {
        std::string operation = std::string(OPERATION_NAME);
        operation.append("(\"");
        operation.append(prop_name);
        operation.append("\")");
        if (!post_auth_request(env, operation)) {
            LOGE("user denies to let process:%d get properties:%s\n", getpid(), prop_name);
            return false;
        }
        LOGD("user allows to let process:%d use properties:%s\n", getpid(), prop_name);
    }
    return true;
}

#define NEED_AUTH(prop_name,target,exclude_process) if(!need_auth(env,prop_name,target,exclude_process)){get_permitted=false;}

#define RANDOM_PROP_VALUE_LENGTH 16

typedef int (*func_sys_get_prop)(const char* __name, char* __value);

static func_sys_get_prop old_sys_get_prop= nullptr;

int my_sys_get_prop(const char* __name, char* __value){
    if(!old_sys_get_prop){
        LOGE("old_sys_get_prop is nullptr\n");
        return JNI_ERR;
    }
    bool get_permitted=true;
    if (strstr(__name, "serial") != nullptr) {
        LOGD("before sys_get_prop:%s\n",__name);
        if (!is_system_process()) {
            JNIEnv *env = get_jni_env();
            NEED_AUTH(__name, "serial", "zygote");
        } else {
            __value = generate_random_prop_value(RANDOM_PROP_VALUE_LENGTH);
            LOGE("policy denies to let process:%d get properties:%s\n", getpid(), __name);
            return RANDOM_PROP_VALUE_LENGTH;
        }
    }
    if(!get_permitted){
        __value = generate_random_prop_value(RANDOM_PROP_VALUE_LENGTH);
        return RANDOM_PROP_VALUE_LENGTH;
    }
    return old_sys_get_prop(__name,__value);
}

typedef int (*func_sys_read_prop)(const prop_info* __pi, char* __name, char* __value);

static func_sys_read_prop old_sys_read_prop= nullptr;

int my_sys_read_prop(const prop_info* __pi, char* __name, char* __value){
    if(!old_sys_read_prop){
        LOGE("old_sys_read_prop is nullptr\n");
        return JNI_ERR;
    }
    int ret_prop_length= old_sys_read_prop(__pi,__name,__value);
    LOGD("before sys_read_prop:%s\n",__name);
    JNIEnv *env= get_jni_env();
    if(!env) {
        if (strstr(__name, "serial")!= nullptr) {
            __value=generate_random_prop_value(RANDOM_PROP_VALUE_LENGTH);
            LOGE("policy denies to let process:%d get properties:%s\n", getpid(), __name);
            return RANDOM_PROP_VALUE_LENGTH;
        }
    }
    bool get_permitted=true;
    NEED_AUTH(__name,"serial", nullptr);
    if(!get_permitted){
        __value=generate_random_prop_value(RANDOM_PROP_VALUE_LENGTH);
        return RANDOM_PROP_VALUE_LENGTH;
    }
    return ret_prop_length;
}

void hook_sys_get_properties(JNIEnv *env) {
    if(!old_sys_get_prop) {
        void* sys_get_prop_symbol=dlsym(RTLD_DEFAULT,"__system_property_get");
        LOGD("sys_get_prop_symbol:%p\n",sys_get_prop_symbol);
        AKHookFunction(sys_get_prop_symbol,
                       reinterpret_cast<void *>(my_sys_get_prop),
                       reinterpret_cast<void **>(&old_sys_get_prop));
        LOGD("old_sys_get_prop:%p\n", old_sys_get_prop);
    }
    //find -> read -> get
    /*if(!old_sys_read_prop) {
        void* sys_read_prop_symbol=dlsym(RTLD_DEFAULT,"__system_property_read");
        LOGD("sys_read_prop_symbol:%p\n",sys_read_prop_symbol);
        AKHookFunction(reinterpret_cast<void *>(sys_read_prop_symbol),
                       reinterpret_cast<void *>(my_sys_read_prop),
                       reinterpret_cast<void **>(&old_sys_read_prop));
        LOGD("old_sys_read_prop:%p\n", old_sys_read_prop);
    }*/
}

char* generate_random_prop_value(int length) {
    using namespace std;
    default_random_engine e;
    uniform_int_distribution<unsigned> u(48, 122);//from 0-z
    int str_length=length+1;
    char* result=(char*)malloc(str_length);
    memset(result,0,str_length);
    for (int i = 0; i < length; ++i) {
        result[i]=u(e);
    }
    result[length]='\0';
    return result;
}
